#include "data_structures.hpp"
#include <cmath>
#include <algorithm>
#include <iostream>

namespace EddyDetection {

    // input transformer
    void DataTransform::data_to_field(const VelocityData& velocity_data, const TemperatureData& temp_data, VelocityField& velocity_field, TemperatureField& temp_field) {
        size_t n_depth = velocity_data.depth.size();
        size_t n_lat = velocity_data.lat.size();
        size_t n_lon = velocity_data.lon.size();

        // Pre allocated space
        std::vector<Eigen::MatrixXd> u_layers(n_depth, Eigen::MatrixXd(n_lat, n_lon));
        std::vector<Eigen::MatrixXd> v_layers(n_depth, Eigen::MatrixXd(n_lat, n_lon));
        std::vector<Eigen::MatrixXd> temp_layers(n_depth, Eigen::MatrixXd(n_lat, n_lon));

        // Simultaneously fill u, v, and temp
        for (size_t k = 0; k < n_depth; ++k) {
            for (size_t i = 0; i < n_lat; ++i) {
                for (size_t j = 0; j < n_lon; ++j) {
                    u_layers[k](i, j) = velocity_data.u_layers[i][j][k];
                    v_layers[k](i, j) = velocity_data.v_layers[i][j][k];
                    temp_layers[k](i, j) = temp_data.data_layers[i][j][k];
                }
            }
        }

        // Convert coordinate data
        Eigen::VectorXd lat_eigen = Eigen::Map<const Eigen::VectorXd>(velocity_data.lat.data(), n_lat);
        Eigen::VectorXd lon_eigen = Eigen::Map<const Eigen::VectorXd>(velocity_data.lon.data(), n_lon);
        Eigen::VectorXd depth_eigen = Eigen::Map<const Eigen::VectorXd>(velocity_data.depth.data(), n_depth);
        Eigen::VectorXd time_eigen = Eigen::Map<const Eigen::VectorXd>(velocity_data.time.data(), velocity_data.time.size());

        // Create VelocityField TemperatureField
        velocity_field = VelocityField(u_layers, v_layers, lat_eigen, lon_eigen, depth_eigen, time_eigen);
        temp_field = TemperatureField(temp_layers, lat_eigen, lon_eigen, depth_eigen);
    }

    // output transformer with no link 
    void DataTransform::eddy2D_to_2DData(std::vector<std::vector<EddyContourData>>& all_layers_eddiesdata, const std::vector<std::vector<EddyContour>>& all_layers_eddies) {
        // Traverse the eddies data of each layer
        for (const auto& layer_eddies : all_layers_eddies) {
            std::vector<EddyContourData> layer_data;

            // Traverse each eddies in the current layer
            for (const auto& eddy : layer_eddies) {
                EddyContourData data;

                // Eigen::Vector2d -> BoundaryPoint2D
                for (const auto& point : eddy.boundary_points) {
                    data.boundary_points.push_back({ point.x(), point.y() });
                }

                // Conversion center point
                data.center = { eddy.center.x(), eddy.center.y() };

                // Copy other attributes
                data.area_km2 = eddy.area_km2;
                data.radius = eddy.radius;
                data.mean_vorticity = eddy.mean_vorticity;
                data.mean_omega = eddy.mean_omega;
                data.temp_anomaly = eddy.temp_anomaly;
                data.type = eddy.type;
                data.thermal_type = eddy.thermal_type;
                data.id = eddy.id;
                data.depth = eddy.depth;
                data.layer_index = eddy.layer_index;

                // Add data container to the current layer
                layer_data.push_back(data);
            }

            // Add the data of the current layer to the output container
            all_layers_eddiesdata.push_back(layer_data);
        }
    }

    // output transformer
    void DataTransform::eddies_to_eddiesdata(std::vector<Eddy3DData>& eddiesdata_3d, const std::vector<Eddy3D>& eddies_3d) {
        for (const auto& eddy_3d : eddies_3d) {
            Eddy3DData eddy_data;

            // Basic information
            eddy_data.id = eddy_3d.id;
            eddy_data.type = eddy_3d.type;
            eddy_data.layer_count = eddy_3d.layer_count;
            eddy_data.volume_km3 = eddy_3d.volume_km3;

            // 3D center point
            eddy_data.center_3d.lat = eddy_3d.center_3d.x();
            eddy_data.center_3d.lon = eddy_3d.center_3d.y();
            eddy_data.center_3d.depth = eddy_3d.center_3d.z();

            // Vertical range
            eddy_data.vertical_extent.x = eddy_3d.vertical_extent.x();
            eddy_data.vertical_extent.y = eddy_3d.vertical_extent.y();

            // Eddies data at each layer
            for (const auto& layer : eddy_3d.layers) {
                EddyContourData layer_data;

                // Boundary point
                for (const auto& point : layer.boundary_points) {
                    BoundaryPoint2D boundary_point;
                    boundary_point.x = point.x();
                    boundary_point.y = point.y();
                    layer_data.boundary_points.push_back(boundary_point);
                }

                // Center point
                layer_data.center.x = layer.center.x();
                layer_data.center.y = layer.center.y();

                // Other attributes
                layer_data.area_km2 = layer.area_km2;
                layer_data.mean_vorticity = layer.mean_vorticity;
                layer_data.mean_omega = layer.mean_omega;
                layer_data.temp_anomaly = layer.temp_anomaly;
                layer_data.type = layer.type;
                layer_data.thermal_type = layer.thermal_type;
                layer_data.id = layer.id;
                layer_data.depth = layer.depth;
                layer_data.layer_index = layer.layer_index;

                eddy_data.layers.push_back(layer_data);
            }

            eddiesdata_3d.push_back(eddy_data);
        }
    }
} // namespace EddyDetection 